package yartiss.engine.simulation.scheduling.policies.tools;

import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.ITaskSet;

public class FPSlackTimeManager {

	long slackTimeDate = -1;
	private long slackTime;

	public long getSlackTime(long date, ITaskSet taskSet) {

		// if (isSlackTimeUpToDate(date)) return slackTime; // TODO => remettre
		// ? verifier (reflechir)...
		// n'est correct que si on est sur que les evt sont traite dans le bon
		// ordre...

		slackTimeDate = date;

		slackTime = Long.MAX_VALUE;
		for (ITask task : taskSet)
			slackTime = Math.min(slackTime, computeSlackTime(date, task, taskSet));
		return slackTime;
	}

	// private boolean isSlackTimeUpToDate(long date) {
	// return slackTimeDate == date;
	// }
	//

	private long computeSlackTime(long date, ITask task, ITaskSet tasks) {
		long slack = 0;
		long w1 = date;

		long di = task.getNextDeadline(date,true); // verifier que si la tache a
												// termine, currentJob vaut bien
												// null et/ou le jobid etc.

		while (w1 <= di) {

			// System.err.println("x" + task.getPriority() + '('
			// + Simulator.instance().getDate() + ") = "
			// + task.getNextActivation());
			// System.err.println("d" + task.getPriority() + '('
			// + Simulator.instance().getDate() + ") = "
			// + task.getNextDeadline());

			long w2 = w1;
			w1 = date + slack + getWorkLoadUntil(date, w2, task, tasks);
			if (w1 == w2) {
				long nu = getIdlePeriodLength(date, w2, task, tasks);
				slack += nu;
				w1 += nu + 1;
			}
		}
		return slack;
	}

	/**
	 * Compute the total workload between date and w
	 * 
	 * @param w
	 * @param task
	 * @return the total workload until time w
	 */
	private long getWorkLoadUntil(long systemDate, long w, ITask task, ITaskSet tasks) {

		long inter = 0;
		for (ITask t : tasks.getHeadSet(task, true)) {			
			inter += t.getRemainingCost() + Math.ceil(Math.max(w - t.getNextActivation(systemDate), 0) / (double) t.getPeriod()) * t.getWcet();
		}

		// System.err.println("t=" + Simulator.instance().getDate() + " : W"
		// + task.getPriority() + '(' + w + ")=" + inter);
		return inter;

	}

	private long getIdlePeriodLength(long currentDate, long t, ITask task, ITaskSet tasks) {

		long idle = Math.min(Math.max(0, task.getNextDeadline(currentDate, true) - t), getIdleRightPart(currentDate, t, task, tasks));

		// System.err.println("t=" + Simulator.instance().getDate() + " : v"
		// + task.getPriority() + '(' + t + ")=" + idle);

		return idle;

	}

	private long getIdleRightPart(long currentDate, long w, ITask task, ITaskSet tasks) {

		long value = Long.MAX_VALUE;
		// for (Task t : tasks.tailSet(task)) {
		for (ITask t : tasks.getHeadSet(task, true)) {
			value = Math.min(
					value,
					(long) Math.max(0, Math.ceil((w - t.getNextActivation(currentDate)) / (double) t.getPeriod())) * t.getPeriod()
							+ t.getNextActivation(currentDate) - w);
		}
		return value;

	}

}
